﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;

namespace Common
{
    /// <summary>
    /// 1. discover max, min, avg, mode, counts for table field
    /// 2. infer data type
    /// </summary>
    public class FieldStatsUtil
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dtData"></param>
        /// <returns></returns>
        public static Dictionary<string, FieldStat> GetFieldStats(DataTable dtData)
        {
            if(dtData==null || dtData.Rows.Count==0)
                throw new Exception("Data is empty");

            Dictionary<string,FieldStat> fieldStats=new Dictionary<string, FieldStat>();
            foreach(DataColumn col in dtData.Columns)
            {
                int missingCount = 0;
                object max = null;
                object min = null;
                object avg = null;
                object mode = null;
                Dictionary<object, int> countByValue = new Dictionary<object, int>();
                int distinctCount = 0;
                DataRow[] drNulls = dtData.Select(string.Format("{0} is null", col.ColumnName));
                if(drNulls!=null && drNulls.Length>0)
                    missingCount = drNulls.Length;
                string dataTypeStr = col.DataType.ToString();
                if(FieldDataType.CanSort(FieldDataType.ToDbType(dataTypeStr)))
                {
                    DataRow[] sorted = dtData.Select(string.Format("{0} is not null", col.ColumnName));
                    if (sorted != null && sorted.Length > 0)
                    {
                        min = sorted[0][col.ColumnName];
                        max = sorted[sorted.Length - 1][col.ColumnName];
                    }
                    countByValue=new Dictionary<object, int>();
                    foreach(DataRow dr in sorted)
                    {
                        object val = dr[col.ColumnName];
                        if (!countByValue.ContainsKey(val))
                            countByValue.Add(val, 1);
                        else
                            countByValue[val] = countByValue[val] + 1;
                    }
                    distinctCount = countByValue.Count;
                    int maxCount = 0;
                    foreach(object val in countByValue.Keys)
                    {
                        if(countByValue[val]>maxCount)
                        {
                            maxCount = countByValue[val];
                            mode = val;
                        }
                    }
                    double sum = 0;
                    if (FieldDataType.IsNumber(FieldDataType.ToDbType(dataTypeStr)))
                    {
                        foreach(DataRow dr in sorted)
                        {
                            sum += double.Parse(dr[col.ColumnName].ToString());
                        }
                    }
                    avg = sum/sorted.Length;
                }
                FieldStat fieldStat=new FieldStat();
                fieldStat.Average = avg;
                fieldStat.Count = dtData.Rows.Count;
                fieldStat.DataType = SimpleDataTypeUtil.ToSimpleDataType(col.DataType);
                fieldStat.DistinctValueCount = distinctCount;
                fieldStat.FieldName = col.ColumnName;
                fieldStat.MaxValue = max;
                fieldStat.MinValue = min;
                fieldStat.MissingValueCount = missingCount;
                fieldStat.Mode = mode;
                if(fieldStat.DistinctValueCount>10)
                    fieldStat.Distributions = countByValue;
                else
                    fieldStat.Distributions = new Dictionary<object, int>();
                
                fieldStats.Add(col.ColumnName, fieldStat);
            }
            return fieldStats;
        }
    }
}
